package com.defangit.production.orm;

import com.defangit.production.bll.ProductionBillBll;
import com.defangit.production.domain.*;
import laogen.hibernate.CommonFinder;
import laogen.hibernate.CommonService;
import laogen.lang.exception.ParameterNotValidException;
import laogen.util.UniqueNoBuilder;
import org.hibernate.Criteria;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import java.time.LocalDateTime;
import java.util.List;

/**
 * Created by zhangfagen on 2018/6/6.
 */
public class ProductionBillService extends CommonService<ProductionBill> implements ProductionBillBll {
    private static UniqueNoBuilder noBuilder = UniqueNoBuilder.getInstance("production.bill", (nos) -> {
        List<String> exists = HibernateUtil
                .INSTANCE
                .getSessionFactory()
                .getCurrentSession()
                .createCriteria(ProductionBill.class)
                .add(Restrictions.in("no", nos))
                .setProjection(Projections.property("no"))
                .list();
        nos.removeAll(exists);
    });

    public ProductionBill addProductionBill(ProductionBill bill) {
        if(bill==null) throw ParameterNotValidException.nullParameter("生产订单");
        if (bill.getExpectEndTime()!=null&&bill.getExpectEndTime().isBefore(LocalDateTime.now())) {
            throw ParameterNotValidException.constraintNotSatisfy("预期结束时间异常","请重设正常的计划时间");
        }
        add(bill);
        String _no=noBuilder.next();
        bill.setNo(_no);
        /*bill.getProcesses().forEach(process->{
            process.setNo(_no+processNoBuilder.next());
        });*/
        commitSession();
        return get(bill.getId()).orElseThrow(()->
                ParameterNotValidException.zeroIdOrEntityNonExist("生产订单")
        );
    }


    public ProductionBill updateProductionBill(ProductionBill bill,Integer operatorId) {
        if(bill==null) throw ParameterNotValidException.nullParameter("生产订单");
        ProductionBill productionBill = get(bill.getId()).orElseThrow(()->ParameterNotValidException.zeroIdOrEntityNonExist("生产订单信息异常"));
        Operator operator = new OperatorService().get(operatorId).orElseThrow(()->ParameterNotValidException.zeroIdOrEntityNonExist("用户信息异常"));
        if (!operator.getTags().contains(2)) throw ParameterNotValidException.constraintNotSatisfy("该用户权限不足","您的权限不足");
        productionBill.setLiable(operator);
        if (bill.getExpectEndTime()!=null){
            productionBill.setExpectEndTime(bill.getExpectEndTime());
        }
        merge(bill);
        commitSession();
        return get(bill.getId()).orElseThrow(()->
            ParameterNotValidException.zeroIdOrEntityNonExist("生产订单")
        );
    }

    public boolean deleteProductionBill(int id) {
        return false;
    }

    @Override
    public ProductionBill updateExpectTime(LocalDateTime exportTime, Integer billId, Integer liableId) {
        ProductionBill productionBill = get(billId).orElseThrow(()->ParameterNotValidException.zeroIdOrEntityNonExist("生产订单的信息获取异常"));
        Operator liable = new OperatorService().get(liableId).orElseThrow(()->ParameterNotValidException.zeroIdOrEntityNonExist("订单负责人信息异常"));
        if (liable.getId()!=productionBill.getLiable().getId()) throw ParameterNotValidException.constraintNotSatisfy("负责人与生产订单不匹配,无法完成本次操作","the liable is invalid");
        if (productionBill.getEndTime()!=null) throw ParameterNotValidException.constraintNotSatisfy("已完成的订单不可更改计划时间","can not update time");
        if (exportTime.isBefore(productionBill.getStartTime())) throw ParameterNotValidException.constraintNotSatisfy("生产订单的计划完成时间不能在开启时间之前,请重新设置","this time is invalid");
        productionBill.setExpectEndTime(exportTime);
        update(productionBill);
        commitSession();
        return get(billId).orElseThrow(()->ParameterNotValidException.zeroIdOrEntityNonExist("生产订单的信息获取异常"));
    }

    public ProductionBill processId(Integer processId){
        ProductionBill productionBill = (ProductionBill) detachedAll().createAlias("processes","process")
                .add(Restrictions.eq("process",processId)).getExecutableCriteria(getSession()).uniqueResult();
        if (productionBill==null) throw ParameterNotValidException.constraintNotSatisfy("没有查找到对应的生产订单","we can't get this productionBill from this id of process");
        return productionBill;
    }

    public ProductionBill updateStartTime(Integer operatorId,Integer billId) {
        ProductionBill productionbill = get(billId).orElseThrow(()->ParameterNotValidException.zeroIdOrEntityNonExist("生产订单信息异常"));
        Operator operator = new OperatorService().get(operatorId).orElseThrow(()->ParameterNotValidException.zeroIdOrEntityNonExist("用户信息异常"));
        productionbill.setStartTime(LocalDateTime.now());
        update(productionbill);
        commitSession();
        return productionbill;
    }

    public ProductionBill updateEndTime(Integer operatorId,Integer billId) {
        ProductionBill productionbill = get(billId).orElseThrow(()->ParameterNotValidException.zeroIdOrEntityNonExist("生产订单信息异常"));
        Operator operator = new OperatorService().get(operatorId).orElseThrow(()->ParameterNotValidException.zeroIdOrEntityNonExist("用户信息异常"));
        productionbill.setEndTime(LocalDateTime.now());
        update(productionbill);
        commitSession();
        return productionbill;
    }

    /*生产主管确认订单*/
    @Override
    public ProductionBill updateBillAddLiable(Integer bill, Integer leaderId) {
        ProductionBill productionBill = get(bill).orElseThrow(()->ParameterNotValidException.zeroIdOrEntityNonExist("待确认订单信息异常"));
        Operator leader = new OperatorService().get(leaderId).orElseThrow(()->ParameterNotValidException.zeroIdOrEntityNonExist("用户信息异常"));
        productionBill.setLiable(leader);
        update(productionBill);
        commitSession();
        return productionBill;
    }

    @Override
    public ProductionBillFinder findAll() {
        return new ProductionBillFinderImpl();
    }

    public class ProductionBillFinderImpl extends CommonFinder<ProductionBill> implements ProductionBillFinder{
        private DetachedCriteria criteria;
        ProductionBillFinderImpl(){
            this.criteria=detachedAll();
        }
        public Criteria getCriteria() {
            return criteria.getExecutableCriteria(getSession()).addOrder(Order.desc("id"));
        }

        public DetachedCriteria getDetachedCriteria() {
            return criteria;
        }

        @Override
        public ProductionBillFinder authorId(Integer id) {
            if (id == null) return this;
            criteria = criteria.add(Restrictions.eq("author.id",id));
            return this;
        }

        @Override
        public ProductionBillFinder liableId(Integer id) {
            if (id == null) return this;
            criteria = criteria.add(Restrictions.eq("liable.id",id));
            return this;
        }

        @Override
        public ProductionBillFinder processLiable(Integer id) {
            if (id == null) return this;
            criteria = criteria.createAlias("processes","process").add(Restrictions.eq("process.liable.id",id));
            return this;
        }

        @Override
        public ProductionBillFinder liableIsNotNull() {
            criteria = criteria.add(Restrictions.isNotNull("liable"));
            return this;
        }

        @Override
        public ProductionBillFinder startTimeIsNotNull() {
            criteria = criteria.add(Restrictions.isNotNull("startTime"));
            return this;
        }

        @Override
        public ProductionBillFinder endTimeIsNotNull() {
            criteria = criteria.add(Restrictions.isNotNull("endTime"));
            return this;
        }
    }
}
